home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / FFConst / ffconst.cpp next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  18.4 KB  |  561 lines

  1. //-----------------------------------------------------------------------------
  2. // File: FFConst.cpp
  3. //
  4. // Desc: Demonstrates an application which sets a force feedback constant force 
  5. //       determined by the user.
  6. //
  7. // Copyright (c) Microsoft Corporation. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #define DIRECTINPUT_VERSION 0x0800
  11.  
  12. #include <tchar.h>
  13. #include <windows.h>
  14. #include <windowsx.h>
  15. #include <commctrl.h>
  16. #include <basetsd.h>
  17. #include <mmsystem.h>
  18. #include <dinput.h>
  19. #include <math.h>
  20. #include "resource.h"
  21.  
  22.  
  23.  
  24.  
  25. //-----------------------------------------------------------------------------
  26. // Function prototypes 
  27. //-----------------------------------------------------------------------------
  28. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  29. BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, VOID* pContext );
  30. BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext );
  31. HRESULT InitDirectInput( HWND hDlg );
  32. VOID    FreeDirectInput();
  33. VOID    OnPaint( HWND hDlg );
  34. HRESULT OnMouseMove( HWND hDlg, INT x, INT y, UINT keyFlags );
  35. VOID    OnLeftButtonDown( HWND hDlg, INT x, INT y, UINT keyFlags );
  36. VOID    OnLeftButtonUp( HWND hDlg, INT x, INT y, UINT keyFlags );
  37. INT     CoordToForce( INT x );
  38. HRESULT SetDeviceForcesXY();
  39.  
  40.  
  41.  
  42.  
  43. //-----------------------------------------------------------------------------
  44. // Defines, constants, and global variables
  45. //-----------------------------------------------------------------------------
  46. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  47. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  48.  
  49. #define FEEDBACK_WINDOW_X       20
  50. #define FEEDBACK_WINDOW_Y       60
  51. #define FEEDBACK_WINDOW_WIDTH   200
  52.  
  53. LPDIRECTINPUT8        g_pDI       = NULL;         
  54. LPDIRECTINPUTDEVICE8  g_pDevice   = NULL;
  55. LPDIRECTINPUTEFFECT   g_pEffect   = NULL;
  56. BOOL                  g_bActive   = TRUE;
  57. DWORD                 g_dwNumForceFeedbackAxis = 0;
  58. INT                   g_nXForce;
  59. INT                   g_nYForce;
  60. DWORD                 g_dwLastEffectSet; // Time of the previous force feedback effect set
  61.  
  62.  
  63.  
  64.  
  65. //-----------------------------------------------------------------------------
  66. // Name: WinMain()
  67. // Desc: Entry point for the application.  Since we use a simple dialog for 
  68. //       user interaction we don't need to pump messages.
  69. //-----------------------------------------------------------------------------
  70. INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
  71. {
  72.     InitCommonControls();
  73.  
  74.     // Display the main dialog box.
  75.     DialogBox( hInst, MAKEINTRESOURCE(IDD_FORCE_FEEDBACK), NULL, MainDlgProc );
  76.     
  77.     return TRUE;
  78. }
  79.  
  80.  
  81.  
  82.  
  83. //-----------------------------------------------------------------------------
  84. // Name: MainDlgProc
  85. // Desc: Handles dialog messages
  86. //-----------------------------------------------------------------------------
  87. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  88. {
  89.     switch( msg ) 
  90.     {
  91.         case WM_INITDIALOG:
  92.             if( FAILED( InitDirectInput( hDlg ) ) ) 
  93.             {
  94.                 MessageBox( NULL, _T("Error Initializing DirectInput ") 
  95.                                   _T("The sample will now exit."), 
  96.                                   _T("FFConst"), MB_ICONERROR | MB_OK );
  97.                 EndDialog( hDlg, 0 );
  98.             }
  99.  
  100.             // Init the time of the last force feedback effect
  101.             g_dwLastEffectSet = timeGetTime();
  102.             break;
  103.  
  104.         case WM_MOUSEMOVE:
  105.             if( FAILED( OnMouseMove( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam ) ) )
  106.             {
  107.                 MessageBox( NULL, _T("Error setting effect parameters. ")
  108.                                   _T("The sample will now exit."), 
  109.                                   _T("FFConst"), MB_ICONERROR | MB_OK );
  110.                 EndDialog( hDlg, 0 );
  111.             }
  112.             break;
  113.  
  114.         case WM_LBUTTONDOWN:
  115.             OnLeftButtonDown( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam );
  116.             break;
  117.  
  118.         case WM_LBUTTONUP:
  119.             OnLeftButtonUp( hDlg, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT)wParam );
  120.             break;
  121.  
  122.         case WM_PAINT:
  123.             OnPaint( hDlg );
  124.             break;
  125.  
  126.         case WM_ACTIVATE:
  127.             if( WA_INACTIVE != wParam && g_pDevice )
  128.             {
  129.                 // Make sure the device is acquired, if we are gaining focus.
  130.                 g_pDevice->Acquire();
  131.  
  132.                 if( g_pEffect ) 
  133.                     g_pEffect->Start( 1, 0 ); // Start the effect
  134.             }
  135.             break;
  136.  
  137.         case WM_COMMAND:
  138.             switch( LOWORD(wParam) )
  139.             {
  140.                 case IDCANCEL:
  141.                     EndDialog( hDlg, 0 );
  142.                     break;
  143.  
  144.                 default:
  145.                     return FALSE; // Message not handled 
  146.             }
  147.             break;
  148.  
  149.         case WM_DESTROY:
  150.             // Cleanup everything
  151.             KillTimer( hDlg, 0 );    
  152.             FreeDirectInput();    
  153.             break;
  154.  
  155.         default:
  156.             return FALSE; // Message not handled 
  157.     }
  158.  
  159.     return TRUE; // Message handled 
  160. }
  161.  
  162.  
  163.  
  164.  
  165. //-----------------------------------------------------------------------------
  166. // Name: InitDirectInput()
  167. // Desc: Initialize the DirectInput variables.
  168. //-----------------------------------------------------------------------------
  169. HRESULT InitDirectInput( HWND hDlg )
  170. {
  171.     DIPROPDWORD dipdw;
  172.     HRESULT     hr;
  173.  
  174.     // Register with the DirectInput subsystem and get a pointer
  175.     // to a IDirectInput interface we can use.
  176.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  177.                                          IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
  178.     {
  179.         return hr;
  180.     }
  181.     
  182.     // Look for a force feedback device we can use
  183.     if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, 
  184.                                          EnumFFDevicesCallback, NULL, 
  185.                                          DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK ) ) )
  186.     {
  187.         return hr;
  188.     }
  189.  
  190.     if( NULL == g_pDevice )
  191.     {
  192.         MessageBox( NULL, _T("Force feedback device not found. ")
  193.                           _T("The sample will now exit."), 
  194.                           _T("FFConst"), MB_ICONERROR | MB_OK );
  195.         EndDialog( hDlg, 0 );
  196.         return S_OK;
  197.     }
  198.  
  199.     // Set the data format to "simple joystick" - a predefined data format. A
  200.     // data format specifies which controls on a device we are interested in,
  201.     // and how they should be reported.
  202.     //
  203.     // This tells DirectInput that we will be passing a DIJOYSTATE structure to
  204.     // IDirectInputDevice8::GetDeviceState(). Even though we won't actually do
  205.     // it in this sample. But setting the data format is important so that the
  206.     // DIJOFS_* values work properly.
  207.     if( FAILED( hr = g_pDevice->SetDataFormat( &c_dfDIJoystick ) ) )
  208.         return hr;
  209.  
  210.     // Set the cooperative level to let DInput know how this device should
  211.     // interact with the system and with other DInput applications.
  212.     // Exclusive access is required in order to perform force feedback.
  213.     if( FAILED( hr = g_pDevice->SetCooperativeLevel( hDlg,
  214.                                                      DISCL_EXCLUSIVE | 
  215.                                                      DISCL_FOREGROUND ) ) )
  216.     {
  217.         return hr;
  218.     }
  219.  
  220.     // Since we will be playing force feedback effects, we should disable the
  221.     // auto-centering spring.
  222.     dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  223.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  224.     dipdw.diph.dwObj        = 0;
  225.     dipdw.diph.dwHow        = DIPH_DEVICE;
  226.     dipdw.dwData            = FALSE;
  227.  
  228.     if( FAILED( hr = g_pDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ) ) )
  229.         return hr;
  230.  
  231.     // Enumerate and count the axes of the joystick 
  232.     if ( FAILED( hr = g_pDevice->EnumObjects( EnumAxesCallback, 
  233.                                               (VOID*)&g_dwNumForceFeedbackAxis, DIDFT_AXIS ) ) )
  234.         return hr;
  235.  
  236.     // This simple sample only supports one or two axis joysticks
  237.     if( g_dwNumForceFeedbackAxis > 2 )
  238.         g_dwNumForceFeedbackAxis = 2;
  239.  
  240.     // This application needs only one effect: Applying raw forces.
  241.     DWORD           rgdwAxes[2]     = { DIJOFS_X, DIJOFS_Y };
  242.     LONG            rglDirection[2] = { 0, 0 };
  243.     DICONSTANTFORCE cf              = { 0 };
  244.  
  245.     DIEFFECT eff;
  246.     ZeroMemory( &eff, sizeof(eff) );
  247.     eff.dwSize                  = sizeof(DIEFFECT);
  248.     eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
  249.     eff.dwDuration              = INFINITE;
  250.     eff.dwSamplePeriod          = 0;
  251.     eff.dwGain                  = DI_FFNOMINALMAX;
  252.     eff.dwTriggerButton         = DIEB_NOTRIGGER;
  253.     eff.dwTriggerRepeatInterval = 0;
  254.     eff.cAxes                   = g_dwNumForceFeedbackAxis;
  255.     eff.rgdwAxes                = rgdwAxes;
  256.     eff.rglDirection            = rglDirection;
  257.     eff.lpEnvelope              = 0;
  258.     eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
  259.     eff.lpvTypeSpecificParams   = &cf;
  260.     eff.dwStartDelay            = 0;
  261.  
  262.     // Create the prepared effect
  263.     if( FAILED( hr = g_pDevice->CreateEffect( GUID_ConstantForce, 
  264.                                               &eff, &g_pEffect, NULL ) ) )
  265.     {
  266.         return hr;
  267.     }
  268.  
  269.     if( NULL == g_pEffect )
  270.         return E_FAIL;
  271.  
  272.     return S_OK;
  273. }
  274.  
  275.  
  276.  
  277.  
  278. //-----------------------------------------------------------------------------
  279. // Name: EnumAxesCallback()
  280. // Desc: Callback function for enumerating the axes on a joystick and counting
  281. //       each force feedback enabled axis
  282. //-----------------------------------------------------------------------------
  283. BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
  284.                                 VOID* pContext )
  285. {
  286.     DWORD* pdwNumForceFeedbackAxis = (DWORD*) pContext;
  287.  
  288.     if( (pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0 )
  289.         (*pdwNumForceFeedbackAxis)++;
  290.  
  291.     return DIENUM_CONTINUE;
  292. }
  293.  
  294.  
  295.  
  296.  
  297. //-----------------------------------------------------------------------------
  298. // Name: EnumFFDevicesCallback()
  299. // Desc: Called once for each enumerated force feedback device. If we find
  300. //       one, create a device interface on it so we can play with it.
  301. //-----------------------------------------------------------------------------
  302. BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, 
  303.                                      VOID* pContext )
  304. {
  305.     LPDIRECTINPUTDEVICE8 pDevice;
  306.     HRESULT              hr;
  307.  
  308.     // Obtain an interface to the enumerated force feedback device.
  309.     hr = g_pDI->CreateDevice( pInst->guidInstance, &pDevice, NULL );
  310.  
  311.     // If it failed, then we can't use this device for some
  312.     // bizarre reason.  (Maybe the user unplugged it while we
  313.     // were in the middle of enumerating it.)  So continue enumerating
  314.     if( FAILED(hr) ) 
  315.         return DIENUM_CONTINUE;
  316.  
  317.     // We successfully created an IDirectInputDevice8.  So stop looking 
  318.     // for another one.
  319.     g_pDevice = pDevice;
  320.  
  321.     return DIENUM_STOP;
  322. }
  323.  
  324.  
  325.  
  326.  
  327. //-----------------------------------------------------------------------------
  328. // Name: FreeDirectInput()
  329. // Desc: Initialize the DirectInput variables.
  330. //-----------------------------------------------------------------------------
  331. VOID FreeDirectInput()
  332. {
  333.     // Unacquire the device one last time just in case 
  334.     // the app tried to exit while the device is still acquired.
  335.     if( g_pDevice ) 
  336.         g_pDevice->Unacquire();
  337.     
  338.     // Release any DirectInput objects.
  339.     SAFE_RELEASE( g_pEffect );
  340.     SAFE_RELEASE( g_pDevice );
  341.     SAFE_RELEASE( g_pDI );
  342. }
  343.  
  344.  
  345.  
  346.  
  347. //-----------------------------------------------------------------------------
  348. // Name: OnPaint()
  349. // Desc: Handles the WM_PAINT window message
  350. //-----------------------------------------------------------------------------
  351. VOID OnPaint( HWND hDlg )
  352. {
  353.     PAINTSTRUCT ps;
  354.     HDC         hDC;
  355.     HPEN        hpenOld;
  356.     HPEN        hpenBlack;
  357.     HBRUSH      hbrOld;
  358.     HBRUSH      hbrBlack;
  359.     INT         x;
  360.     INT         y;
  361.     
  362.     hDC = BeginPaint( hDlg, &ps );
  363.     if( NULL == hDC ) 
  364.         return;
  365.  
  366.     // Everything is scaled to the size of the window.
  367.     hpenBlack = GetStockPen( BLACK_PEN );
  368.     hpenOld   = SelectPen( hDC, hpenBlack );
  369.  
  370.     // Draw force feedback bounding rect
  371.     MoveToEx( hDC, FEEDBACK_WINDOW_X, FEEDBACK_WINDOW_Y, NULL );
  372.  
  373.     LineTo( hDC, FEEDBACK_WINDOW_X, 
  374.                  FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH );
  375.     LineTo( hDC, FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH, 
  376.                  FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH );
  377.     LineTo( hDC, FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH, 
  378.                  FEEDBACK_WINDOW_Y );
  379.     LineTo( hDC, FEEDBACK_WINDOW_X, 
  380.                  FEEDBACK_WINDOW_Y );
  381.  
  382.     // Calculate center of feedback window for center marker
  383.     x = FEEDBACK_WINDOW_X + FEEDBACK_WINDOW_WIDTH / 2;
  384.     y = FEEDBACK_WINDOW_Y + FEEDBACK_WINDOW_WIDTH / 2;
  385.  
  386.     // Draw center marker
  387.     MoveToEx( hDC, x, y - 10, NULL );
  388.     LineTo(   hDC, x, y + 10 + 1 );
  389.     MoveToEx( hDC, x - 10, y, NULL );
  390.     LineTo(   hDC, x + 10 + 1, y );
  391.  
  392.     hbrBlack = GetStockBrush( BLACK_BRUSH );
  393.     hbrOld   = SelectBrush( hDC, hbrBlack );
  394.  
  395.     x = MulDiv( FEEDBACK_WINDOW_WIDTH,
  396.                 g_nXForce + DI_FFNOMINALMAX, 
  397.                 2 * DI_FFNOMINALMAX );
  398.  
  399.     y = MulDiv( FEEDBACK_WINDOW_WIDTH, 
  400.                 g_nYForce + DI_FFNOMINALMAX, 
  401.                 2 * DI_FFNOMINALMAX );
  402.  
  403.     x += FEEDBACK_WINDOW_X;
  404.     y += FEEDBACK_WINDOW_Y;
  405.  
  406.     Ellipse( hDC, x-5, y-5, x+6, y+6 );
  407.  
  408.     SelectBrush( hDC, hbrOld );
  409.     SelectPen( hDC, hpenOld );
  410.  
  411.     EndPaint( hDlg, &ps );
  412. }
  413.  
  414.  
  415.  
  416.  
  417. //-----------------------------------------------------------------------------
  418. // Name: OnMouseMove()
  419. // Desc: If the mouse button is down, then change the direction of
  420. //       the force to match the new location.
  421. //-----------------------------------------------------------------------------
  422. HRESULT OnMouseMove( HWND hDlg, INT x, INT y, UINT keyFlags )
  423. {
  424.     HRESULT hr;
  425.     DWORD   dwCurrentTime;
  426.  
  427.     if( NULL == g_pEffect )
  428.         return S_OK;
  429.  
  430.     if( keyFlags & MK_LBUTTON ) 
  431.     {
  432.         dwCurrentTime = timeGetTime();
  433.         
  434.         if( dwCurrentTime - g_dwLastEffectSet < 100 )
  435.         {
  436.             // Don't allow setting effect more often than
  437.             // 100ms since every time an effect is set, the
  438.             // device will jerk.
  439.             //
  440.             // Note: This is not neccessary, and is specific to this sample
  441.             return S_OK;
  442.         }
  443.  
  444.         g_dwLastEffectSet = dwCurrentTime;
  445.  
  446.         x -= FEEDBACK_WINDOW_X;
  447.         y -= FEEDBACK_WINDOW_Y;
  448.  
  449.         g_nXForce = CoordToForce( x );
  450.         g_nYForce = CoordToForce( y );
  451.  
  452.         InvalidateRect( hDlg, 0, TRUE );
  453.         UpdateWindow( hDlg );
  454.  
  455.         if( FAILED( hr = SetDeviceForcesXY() ) )
  456.             return hr;
  457.     }
  458.  
  459.     return S_OK;
  460. }
  461.  
  462.  
  463.  
  464.  
  465. //-----------------------------------------------------------------------------
  466. // Name: OnLeftButtonDown()
  467. // Desc: Capture the mouse so we can follow it, and start updating the
  468. //       force information.
  469. //-----------------------------------------------------------------------------
  470. VOID OnLeftButtonDown( HWND hDlg, INT x, INT y, UINT keyFlags )
  471. {
  472.     SetCapture( hDlg );
  473.     OnMouseMove( hDlg, x, y, MK_LBUTTON );
  474. }
  475.  
  476.  
  477.  
  478.  
  479. //-----------------------------------------------------------------------------
  480. // Name: OnLeftButtonUp()
  481. // Desc: Stop capturing the mouse when the button goes up.
  482. //-----------------------------------------------------------------------------
  483. VOID OnLeftButtonUp( HWND hDlg, INT x, INT y, UINT keyFlags )
  484. {
  485.     ReleaseCapture();
  486. }
  487.  
  488.  
  489.  
  490.  
  491. //-----------------------------------------------------------------------------
  492. // Name: CoordToForce()
  493. // Desc: Convert a coordinate 0 <= nCoord <= FEEDBACK_WINDOW_WIDTH 
  494. //       to a force value in the range -DI_FFNOMINALMAX to +DI_FFNOMINALMAX.
  495. //-----------------------------------------------------------------------------
  496. INT CoordToForce( INT nCoord )
  497. {
  498.     INT nForce = MulDiv( nCoord, 2 * DI_FFNOMINALMAX, FEEDBACK_WINDOW_WIDTH )
  499.                  - DI_FFNOMINALMAX;
  500.  
  501.     // Keep force within bounds
  502.     if( nForce < -DI_FFNOMINALMAX ) 
  503.         nForce = -DI_FFNOMINALMAX;
  504.  
  505.     if( nForce > +DI_FFNOMINALMAX ) 
  506.         nForce = +DI_FFNOMINALMAX;
  507.  
  508.     return nForce;
  509. }
  510.  
  511.  
  512.  
  513.  
  514. //-----------------------------------------------------------------------------
  515. // Name: SetDeviceForcesXY()
  516. // Desc: Apply the X and Y forces to the effect we prepared.
  517. //-----------------------------------------------------------------------------
  518. HRESULT SetDeviceForcesXY()
  519. {
  520.     // Modifying an effect is basically the same as creating a new one, except
  521.     // you need only specify the parameters you are modifying
  522.     LONG rglDirection[2] = { 0, 0 };
  523.  
  524.     DICONSTANTFORCE cf;
  525.  
  526.     if( g_dwNumForceFeedbackAxis == 1 )
  527.     {
  528.         // If only one force feedback axis, then apply only one direction and 
  529.         // keep the direction at zero
  530.         cf.lMagnitude = g_nXForce;
  531.         rglDirection[0] = 0;
  532.     }
  533.     else
  534.     {
  535.         // If two force feedback axis, then apply magnitude from both directions 
  536.         rglDirection[0] = g_nXForce;
  537.         rglDirection[1] = g_nYForce;
  538.         cf.lMagnitude = (DWORD)sqrt( (double)g_nXForce * (double)g_nXForce +
  539.                                      (double)g_nYForce * (double)g_nYForce );
  540.     }
  541.  
  542.     DIEFFECT eff;
  543.     ZeroMemory( &eff, sizeof(eff) );
  544.     eff.dwSize                = sizeof(DIEFFECT);
  545.     eff.dwFlags               = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
  546.     eff.cAxes                 = g_dwNumForceFeedbackAxis;
  547.     eff.rglDirection          = rglDirection;
  548.     eff.lpEnvelope            = 0;
  549.     eff.cbTypeSpecificParams  = sizeof(DICONSTANTFORCE);
  550.     eff.lpvTypeSpecificParams = &cf;
  551.     eff.dwStartDelay            = 0;
  552.  
  553.     // Now set the new parameters and start the effect immediately.
  554.     return g_pEffect->SetParameters( &eff, DIEP_DIRECTION |
  555.                                            DIEP_TYPESPECIFICPARAMS |
  556.                                            DIEP_START );
  557. }
  558.  
  559.  
  560.  
  561.